package com.path.planning.util.path;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.referencing.GeodeticCalculator;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.operation.linemerge.LineMerger;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class GeoUtils {
    /**
     * 84坐标系的 SRID：4326，
     * 坐标精度 指定使用双精度浮点型（FLOATING）
     */
    public static final GeometryFactory GLOBAL_GEOMETRY_FACTORY = new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), 4490);

    public static final GeometryJSON GLOBAL_GEOMETRY_JSON = new GeometryJSON(8);


    /**
     * 计算两点间距离(84坐标系)
     *
     * @param lon1
     * @param lat1
     * @param lon2
     * @param lat2
     * @return
     */
    public static double calculateDis(double lon1, double lat1, double lon2, double lat2) {
        GeodeticCalculator calculator = new GeodeticCalculator(DefaultGeographicCRS.WGS84);
        calculator.setStartingGeographicPoint(lon1, lat1);
        calculator.setDestinationGeographicPoint(lon2, lat2);
        return calculator.getOrthodromicDistance();
    }

    /**
     * 计算两点间距离(84坐标系)
     *
     * @return
     */
    public static double calculateDis(Point p1, Point p2) {
        GeodeticCalculator calculator = new GeodeticCalculator(DefaultGeographicCRS.WGS84);
        calculator.setStartingGeographicPoint(p1.getX(), p1.getY());
        calculator.setDestinationGeographicPoint(p2.getX(), p2.getY());
        return calculator.getOrthodromicDistance();
    }

    /**
     * 根据geometry对象获取geoJson字符串
     *
     * @param geometry geometry对象
     * @return geoJson字符串
     */
    public static String getGeoJsonStrFromGeometry(Geometry geometry) {
        StringWriter writer = new StringWriter();
        try {
            GLOBAL_GEOMETRY_JSON.write(geometry, writer);
        } catch (IOException e) {
            // LOGGER.error();
            e.printStackTrace();
            return StrUtil.EMPTY;
        }
        return writer.toString();
    }

    /**
     * 根据geoJson字符串创建geometry对象
     *
     * @param geoJsonStr geoJson字符串
     * @return geometry对象
     */
    public static Geometry buildGeometryFromGeoJsonStr(String geoJsonStr) {
        try {
            return GLOBAL_GEOMETRY_JSON.read(new ByteArrayInputStream(geoJsonStr.getBytes()));
        } catch (IOException e) {
//            LOGGER.error();
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据坐标数组创建Point对象
     *
     * @param coordinates 坐标点数组
     * @return 点 {@link Point}
     */
    public static Point buildPointFromCoordinates(String[][] coordinates) {
        return GLOBAL_GEOMETRY_FACTORY.createPoint(new Coordinate(Double.parseDouble(coordinates[0][0]), Double.parseDouble(coordinates[0][1])));
    }

    /**
     * 根据坐标数组创建Point对象
     *
     * @param coordinates 坐标点数组
     * @return 点 {@link Point}
     */
    public static Point buildPointFromCoordinates(double[][] coordinates) {
        return GLOBAL_GEOMETRY_FACTORY.createPoint(new Coordinate(coordinates[0][0], coordinates[0][1]));
    }

    /**
     * 根据坐标数组创建LineString对象
     *
     * @param coordinates 坐标点数组
     * @return LineString {@link Point}
     */
    public static LineString buildLineStringFromCoordinates(double[][] coordinates) {
        Coordinate[] coordinateArr = new Coordinate[coordinates.length];
        for (int i = 0; i < coordinates.length; i++) {
            coordinateArr[i] = new Coordinate(coordinates[i][0], coordinates[i][1]);
        }
        return GLOBAL_GEOMETRY_FACTORY.createLineString(coordinateArr);
    }

    /**
     * 根据坐标数组创建LineString对象
     *
     * @param coordinates 坐标点数组{@link Coordinate}
     * @return LineString {@link Point}
     */
    public static LineString buildLineStringFromCoordinates(Coordinate[] coordinates) {
        return GLOBAL_GEOMETRY_FACTORY.createLineString(coordinates);
    }

    /**
     * 根据坐标数组创建MultiLineString对象
     *
     * @param coordinates 坐标点数组
     * @return 线 {@link MultiLineString}
     */
    public static MultiLineString buildMultiLineStringFromCoordinates(String[][] coordinates) {
        Map<String, Object> geometryMap = new HashMap<>();
        double[][] doubleCoordinates = new double[coordinates.length][coordinates[0].length];
        for (int i = 0; i < coordinates.length; i++) {
            for (int j = 0; j < coordinates[0].length; j++) {
                doubleCoordinates[i][j] = Double.parseDouble(coordinates[i][j]);
            }
        }
        geometryMap.put("type", "MultiLineString");
        geometryMap.put("coordinates", doubleCoordinates);
        return (MultiLineString) buildGeometryFromGeoJsonStr(JSONUtil.toJsonStr(geometryMap));
    }

    /**
     * 根据多个LineString创建MultiLineString对象
     *
     * @param lineStrings 单线数组
     * @return 多线 {@link MultiLineString}
     */
    public static MultiLineString buildMultiLineStringByLineStrings(LineString[] lineStrings) {
        return GLOBAL_GEOMETRY_FACTORY.createMultiLineString(lineStrings);
    }

    /**
     * reverse multiLineString
     *
     * @param multiLineString 多线对象
     * @return 反转后的多线
     */
    public static MultiLineString reverseMultiLineString(MultiLineString multiLineString) {
        if (null != multiLineString) {
            int lineNums = multiLineString.getNumGeometries();
            if (lineNums > 0) {
                LineString[] lineStrings = new LineString[lineNums];
                for (int i = 0; i < lineNums; i++) {
                    LineString lineString = (LineString) multiLineString.getGeometryN(i);
                    if (null != lineString) lineStrings[lineNums - i - 1] = lineString.reverse();
                }
                return GLOBAL_GEOMETRY_FACTORY.createMultiLineString(lineStrings);
            }
        }
        return multiLineString;
    }

    /**
     * 根据坐标数组创建Polygon对象
     *
     * @param coordinates 坐标点数组
     * @return 区 {@link Polygon}
     */
    public static Polygon buildPolygonFromCoordinates(String[][] coordinates) {
        Coordinate[] coordinateArr = new Coordinate[coordinates.length];
        for (int i = 0; i < coordinates.length; i++) {
            coordinateArr[i] = new Coordinate(Double.parseDouble(coordinates[i][0]), Double.parseDouble(coordinates[i][1]));
        }
        return GLOBAL_GEOMETRY_FACTORY.createPolygon(coordinateArr);
    }

    /**
     * 根据坐标数组创建Polygon对象
     *
     * @param coordinates 坐标点数组
     * @return 区 {@link Polygon}
     */
    public static Polygon buildPolygonFromCoordinates(double[][] coordinates) {
        Coordinate[] coordinateArr = new Coordinate[coordinates.length];
        for (int i = 0; i < coordinates.length; i++) {
            coordinateArr[i] = new Coordinate(coordinates[i][0], coordinates[i][1], 0.0);
        }
        return GLOBAL_GEOMETRY_FACTORY.createPolygon(coordinateArr);
    }

    /**
     * 根据坐标数组创建 多Polygon对象
     *
     * @param polygons Polygon数组
     * @return 多区 {@link Polygon}
     */
    public static MultiPolygon buildMultiPolygonFromPolygon(Polygon[] polygons) {
        return GLOBAL_GEOMETRY_FACTORY.createMultiPolygon(polygons);
    }

    /**
     * 合并 多线
     *
     * @param lineStrings 多线集合
     * @return 合并后的线
     */
    public static LineString unionMultiLineString(List<MultiLineString> lineStrings) {
        LineMerger lineMerger = new LineMerger();
        lineMerger.add(lineStrings);
        List<Geometry> mergedLineStrings = new ArrayList<>(lineMerger.getMergedLineStrings());
        if (CollUtil.isNotEmpty(mergedLineStrings) && mergedLineStrings.size() == 1 && mergedLineStrings.get(0) instanceof LineString) {
            return (LineString) mergedLineStrings.get(0);
        }
        return null;
    }

    /**
     * 合并 多个 单线 为一个单线
     *
     * @param lineStrings 多线集合
     * @return 合并后的线
     */
    public static LineString unionLineString(List<LineString> lineStrings) {
        LineMerger lineMerger = new LineMerger();
        lineMerger.add(lineStrings);
        List<Geometry> mergedLineStrings = new ArrayList<>(lineMerger.getMergedLineStrings());
        if (CollUtil.isNotEmpty(mergedLineStrings) && mergedLineStrings.size() == 1 && mergedLineStrings.get(0) instanceof LineString) {
            return (LineString) mergedLineStrings.get(0);
        }
        return null;
    }

    /**
     * 判断 点线、线线、线面 是否有交集
     *
     * @param geometry        待对比数据(点、线、面)
     * @param multiLineString 现有道路线数据
     * @return 是否有交集
     */
    public static boolean isIntersects(Geometry geometry, MultiLineString multiLineString) {
        return multiLineString.intersects(geometry);
    }

}
